package de.rj.urwalking.ui.activities;

import java.util.ArrayList;
import java.util.Collections;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.crashlytics.android.Crashlytics;

import de.rj.urwalking.R;
import de.rj.urwalking.config.Config;
import de.rj.urwalking.config.Constants;
import de.rj.urwalking.data.Experiment;
import de.rj.urwalking.data.LoginResult;
import de.rj.urwalking.data.Participant;
import de.rj.urwalking.data.survey.CreateParticipantSurvey;
import de.rj.urwalking.data.survey.Question;
import de.rj.urwalking.data.survey.Survey;
import de.rj.urwalking.db.ReadWritePostgreSQL;
import de.rj.urwalking.exceptions.LoginException;
import de.rj.urwalking.tasks.LoadExperimentsTask;
import de.rj.urwalking.tasks.LoggerTask;
import de.rj.urwalking.ui.fragments.dialog.ExperimentsDialogFragment;
import de.rj.urwalking.ui.fragments.dialog.LoginDialogFragment;
import de.rj.urwalking.ui.fragments.dialog.RegistrationDialogFragment;
import de.rj.urwalking.ui.fragments.dialog.RegistrationSurveyFragment;
import de.rj.urwalking.ui.fragments.dialog.LoginDialogFragment.LoginDialogListener;
import de.rj.urwalking.ui.fragments.dialog.RegistrationDialogFragment.RegistrationDialogListener;
import de.rj.urwalking.ui.fragments.dialog.RegistrationSurveyFragment.RegistrationSurveyListener;
import de.rj.urwalking.util.DeviceUuidFactory;
import de.rj.urwalking.util.ExperimentManager;
import de.rj.urwalking.util.FileUpload;
import de.rj.urwalking.util.PrefUtil;
import de.rj.urwalking.util.Util;
import de.urwalking.legobaukasten.LegoActivity;

/**
 * Activity that is shown at the start of the application. Takes care of loading
 * necessary data. Manages login, logout and registration.
 * 
 * @author rjj
 * 
 */
public class StartActivity extends SherlockFragmentActivity implements LoginDialogListener, RegistrationDialogListener,
		RegistrationSurveyListener {

	private static final String TAG = Config.TAG;

	private ProgressBar mProgressBar;
	private TextView mLoginStatusTextView;
	private Survey mRegistrationSurvey;
	private Button mButtonOutdoor;
	private Button mButtonIndoor;
	private Button mButtonHybrid;
	private Button mButtonLego;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_start);

		initPrefs();
		initLogging();
		initUI();

		// try to upload files
		Log.i(Config.UPLOADTAG, "Try to upload files on start of URWalking.");
		FileUpload.getInstance().start();

		if (Util.hasConnection(this)) {
			Handler handler = setupLoadingHandler();
			new LoadExperimentsTask(this, handler).execute();
		} else {
			showAlert(getString(R.string.no_internet_connection));
			// FIXME: add possibility to retry loading
		}

	}

	private void initPrefs() {
		PreferenceManager.setDefaultValues(this, R.xml.general_preferences, true);
		PreferenceManager.setDefaultValues(this, R.xml.outdoor_preferences, true);
		PreferenceManager.setDefaultValues(this, R.xml.indoor_preferences, true);
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		outState.putSerializable("survey", mRegistrationSurvey);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);
		if (savedInstanceState != null) {
			mRegistrationSurvey = (Survey) savedInstanceState.getSerializable("survey");
		}
	}

	@Override
	protected void onResume() {
		super.onResume();
		checkTestMode();
		updateUI();
	}

	/**
	 * Handles results returned to the FragmentActivity by Google Play services
	 */
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		// Decide what to do based on the original request code
		switch (requestCode) {
		case Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST:
			switch (resultCode) {
			case Activity.RESULT_OK:
				// everything ok, user can now start subactivities
				break;
			}
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getSupportMenuInflater();
		inflater.inflate(R.menu.start_menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.start_menu_login:
			if (ExperimentManager.getInstance().isActiveVersion()) {
				showLoginDialog();
			} else {
				showVersionWarning();
			}
			break;
		case R.id.start_menu_logout:
			PrefUtil.clearCredentials(this);
			updateUI();
			break;
		case R.id.start_menu_register:
			if (ExperimentManager.getInstance().isActiveVersion()) {
				showRegistrationDialog();
			} else {
				showVersionWarning();
			}
			;
			break;
		case R.id.start_menu_preferences:
			startActivity(new Intent(StartActivity.this, GeneralPreferenceActivity.class));
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}

		return true;
	}

	private void initLogging() {

		// only enable logging/crash reporting if flag is set
		if (!Config.CRASH_REPORTING) return;

		Crashlytics.start(this);
		// log the device id to be able to tell them apart later
		DeviceUuidFactory duf = new DeviceUuidFactory(this);
		Crashlytics.setString("id", duf.getDeviceUuid().toString());
		Util.logAvailableMemory(this);

		new LoggerTask().execute();
	}

	private void initUI() {
		mLoginStatusTextView = (TextView) findViewById(R.id.loginStatusTextView);
		mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
		mButtonOutdoor = (Button) findViewById(R.id.buttonOutdoor);
		mButtonOutdoor.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {

				if (!isEverythingReady()) {
					return;
				}
				if (inTestMode()) {
					ExperimentManager.getInstance().chooseTestmodeExperiment();
					Intent intent = new Intent(StartActivity.this, OutdoorActivity.class);
					PrefUtil.saveBoolean("allowViewChange", true, StartActivity.this);					
					PrefUtil.saveInt("experimentType", Experiment.TYPE_OUTDOOR, StartActivity.this);
					startActivity(intent);
				} else {
					showExperimentsDialog(Experiment.TYPE_OUTDOOR, URwalkingActivity.TYPE_OUTDOOR);
				}
			}

		});

		//legobaukasten!
		mButtonIndoor = (Button) findViewById(R.id.buttonIndoor);
		mButtonIndoor.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				if (!isEverythingReady()) {
					return;
				}
				if (inTestMode()) {
					ExperimentManager.getInstance().chooseTestmodeExperiment();
					Intent intent = new Intent(StartActivity.this, IndoorActivity.class);
					PrefUtil.saveBoolean("allowViewChange", false, StartActivity.this);
					PrefUtil.saveInt("experimentType", Experiment.TYPE_INDOOR, StartActivity.this);
					startActivity(intent);
				} else {
					showExperimentsDialog(Experiment.TYPE_INDOOR, URwalkingActivity.TYPE_INDOOR);
				}
			}
		});

		mButtonHybrid = (Button) findViewById(R.id.buttonHybrid);
		mButtonHybrid.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {

				if (!isEverythingReady()) {
					return;
				}
				if (inTestMode()) {
					ExperimentManager.getInstance().chooseTestmodeExperiment();
					Intent intent = new Intent(StartActivity.this, HybridActivity.class);
					PrefUtil.saveBoolean("allowViewChange", true, StartActivity.this);
					PrefUtil.saveInt("experimentType", Experiment.TYPE_OUTDOOR, StartActivity.this);
					startActivity(intent);
				} else {
					showExperimentsDialog(Experiment.TYPE_OUTDOOR, URwalkingActivity.TYPE_HYBRID);
				}
			}
		});
		
		mButtonLego = (Button) findViewById(R.id.buttonLego);
		mButtonLego.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				ExperimentManager.getInstance().chooseTestmodeExperiment();
				Intent intent = new Intent(StartActivity.this, LegoActivity.class);
				PrefUtil.saveBoolean("allowViewChange", true, StartActivity.this);					
				PrefUtil.saveInt("experimentType", Experiment.TYPE_OUTDOOR, StartActivity.this);
				startActivity(intent);
				
			}
		});

		// disable the buttons at startup. They are enabled when the
		// experiments/questions are ready.
		mButtonOutdoor.setEnabled(false);
		mButtonIndoor.setEnabled(false);
	}

	protected void enableButtons() {
		mButtonOutdoor.setEnabled(true);
		mButtonIndoor.setEnabled(true);

	}

	private void updateUI() {

		if (inTestMode()) {
			mLoginStatusTextView.setText(getString(R.string.in_test_mode));
		} else if (loggedIn()) {
			mLoginStatusTextView.setText(getString(R.string.logged_in_as)
					+ PreferenceManager.getDefaultSharedPreferences(this).getString("email", ""));
		} else {
			mLoginStatusTextView.setText(getString(R.string.not_logged_in));
		}

		ActivityCompat.invalidateOptionsMenu(this);
	}

	private boolean inTestMode() {
		return PrefUtil.readBoolean(R.string.key_pref_test_mode, this, false);
	}

	private boolean loggedIn() {
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		return prefs.getInt("participantId", -1) > 0;
	}

	private void checkTestMode() {

		if (inTestMode()) {
			SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
			SharedPreferences.Editor editor = sharedPreferences.edit();
			editor.putInt("participantId", Config.TEST_PARTICIPANT_ID);
			editor.commit();
		}
	}

	private void login(Participant participant) {

		SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
		SharedPreferences.Editor editor = sharedPreferences.edit();

		editor.putInt("participantId", participant.getId());
		editor.putString("email", participant.getEmail());
		editor.putBoolean("loggedIn", true);
		editor.commit();
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {

		if (loggedIn()) {
			menu.findItem(R.id.start_menu_login).setVisible(false);
			menu.findItem(R.id.start_menu_logout).setVisible(true);
			menu.findItem(R.id.start_menu_register).setVisible(false);
		} else {
			menu.findItem(R.id.start_menu_login).setVisible(true);
			menu.findItem(R.id.start_menu_logout).setVisible(false);
			menu.findItem(R.id.start_menu_register).setVisible(true);

		}

		return super.onPrepareOptionsMenu(menu);
	}

	private Handler setupLoadingHandler() {
		// Handles messages from the AsyncTask
		Handler handler = new Handler() {
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
				// What did that async task say?
				switch (msg.what) {
				case LoadExperimentsTask.SUCCESS:
					Log.d(TAG, "success");
					enableButtons();
					break;
				case LoadExperimentsTask.FAILURE:
					Log.d(TAG, "failure");

					// FIXME: ask user to retry
					break;
				}
			}
		};
		return handler;
	}

	@SuppressLint("NewApi")
	private void showVersionWarning() {

		AlertDialog.Builder builder = new AlertDialog.Builder(this);

		// set title
		builder.setTitle(R.string.old_version);

		// check the API version to use the right method
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
			builder.setIconAttribute(android.R.attr.alertDialogIcon);
		} else {
			builder.setIcon(android.R.drawable.ic_dialog_alert);
		}

		// set dialog message
		builder.setMessage(R.string.old_version_message).setCancelable(true)
				.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int id) {
						dialog.dismiss();
					}
				}).setPositiveButton(R.string.download_now, new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						downloadLatestVersion();
						dialog.dismiss();
					}
				});

		// create alert dialog
		AlertDialog alertDialog = builder.create();

		// show it
		alertDialog.show();

	}

	@SuppressLint("NewApi")
	protected void downloadLatestVersion() {
		DownloadManager.Request request = new DownloadManager.Request(Uri.parse(Config.LATEST_VERSION_URL));
		request.setDescription("");
		request.setTitle("URwalking");
		// in order for this if to run, you must use the android 3.2 to compile
		// your app
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
			request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
		}
		request.setDestinationInExternalPublicDir(Config.FOLDER_NAME, "urwalking-latest.apk");

		// get download service and enqueue file
		DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
		manager.enqueue(request);
	}

	private void showLoginDialog() {
		FragmentManager fm = getSupportFragmentManager();
		LoginDialogFragment loginDialogFragment = new LoginDialogFragment();
		loginDialogFragment.show(fm, "fragment_login_dialog");
	}

	private void showLoginDialog(LoginResult loginResult) {
		FragmentManager fm = getSupportFragmentManager();
		LoginDialogFragment loginDialogFragment = new LoginDialogFragment();
		Bundle bundle = new Bundle();
		bundle.putSerializable("loginResult", loginResult);
		loginDialogFragment.setArguments(bundle);
		loginDialogFragment.show(fm, "fragment_login_dialog");
	}

	private void showRegistrationDialog() {

		FragmentManager fm = getSupportFragmentManager();
		RegistrationDialogFragment loginDialogFragment = new RegistrationDialogFragment();
		loginDialogFragment.show(fm, "fragment_registration_dialog");

	}

	private void showExperimentsDialog(int experimentType, int activityType) {
		FragmentManager fm = getSupportFragmentManager();
		ExperimentsDialogFragment experimentsDialogFragment = new ExperimentsDialogFragment();
		Bundle bundle = new Bundle();
		bundle.putInt(ExperimentsDialogFragment.EXPERIMENT_TYPE, experimentType);
		bundle.putInt(ExperimentsDialogFragment.ACTIVITY_TYPE, activityType);
		experimentsDialogFragment.setArguments(bundle);
		experimentsDialogFragment.show(fm, "fragment_experiments_dialog");
	}

	private void showAlert(String message) {
		AlertDialog.Builder builder = new AlertDialog.Builder(StartActivity.this);
		builder.setMessage(message).setCancelable(false).setPositiveButton("Ok", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				dialog.cancel();
			}
		});
		AlertDialog alert = builder.create();
		alert.show();
	}

	@Override
	public void onFinishLoginDialog(String email, String password) {
		new CheckCredentialsTask().execute(email, password);
	}

	private class CheckCredentialsTask extends AsyncTask<String, Void, LoginResult> {

		@Override
		protected void onPreExecute() {
			super.onPreExecute();

			mProgressBar.setVisibility(View.VISIBLE);
		}

		@Override
		protected LoginResult doInBackground(String... params) {

			ReadWritePostgreSQL dbWrapper = new ReadWritePostgreSQL();
			if (!dbWrapper.isConnected()) {
				return null;
			}
			LoginResult loginResult = null;
			try {
				loginResult = dbWrapper.checkCredentials(params[0], params[1]);
			} catch (LoginException e) {
				showAlert(getString(R.string.no_database_connection));
				e.printStackTrace();
			}
			dbWrapper.closeConnection();

			return loginResult;

		}

		@Override
		protected void onPostExecute(LoginResult loginResult) {
			super.onPostExecute(loginResult);

			mProgressBar.setVisibility(View.INVISIBLE);

			if (loginResult == null) {
				showAlert(getString(R.string.no_database_connection));
				return;
			}

			if (loginResult instanceof Participant) {
				login((Participant) loginResult);
				updateUI();
			} else {
				showLoginDialog(loginResult);
			}

		}

	}

	@Override
	public void onFinishRegistrationDialog(String email, String password) {
		new PerformRegistrationTask().execute(email, password);
	}

	private class PerformRegistrationTask extends AsyncTask<String, Boolean, ArrayList<Question>> {

		@Override
		protected ArrayList<Question> doInBackground(String... params) {

			ReadWritePostgreSQL dbWrapper = new ReadWritePostgreSQL();
			if (!dbWrapper.isConnected()) {
				return null;
			}

			String email = params[0];
			String password = params[1];

			// check if user with this email already exists
			if (dbWrapper.checkParticipant(email) != -1) {
				publishProgress(false);
				return null;
			}

			// create the participant
			int participantId = dbWrapper.registerParticipant(email, password);
			if (participantId == -1) {
				dbWrapper.closeConnection();
				publishProgress(false);
				return null;
			}

			// save the values
			SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(StartActivity.this);
			SharedPreferences.Editor editor = sharedPreferences.edit();
			editor.putInt("participantId", participantId);
			editor.putString("email", email);
			editor.commit();

			// finally fetch the questions for our short registration survey
			ArrayList<Integer> questionIds = dbWrapper.fetchQuestionIdsFor(Config.CREATE_PARTICIPANT_SURVEY_NR);
			Question.addScales(dbWrapper.fetchScalesFor(questionIds));
			ArrayList<Question> questions = new ArrayList<Question>(dbWrapper.fetchQuestions(questionIds).values());
			Collections.sort(questions);
			dbWrapper.closeConnection();
			return questions;
		}

		@Override
		protected void onProgressUpdate(Boolean... values) {
			super.onProgressUpdate(values);

			Toast.makeText(StartActivity.this, R.string.registration_error_email, Toast.LENGTH_SHORT).show();

		}

		@Override
		protected void onPostExecute(ArrayList<Question> questions) {

			updateUI();
			if (questions != null) {
				// Create the fragment and show it as a dialog.
				Log.d(TAG, "onPostExecute in StartActivity");
				RegistrationSurveyFragment registrationSurveyFragment = new RegistrationSurveyFragment();
				Bundle args = new Bundle();
				args.putSerializable("questions", questions);
				registrationSurveyFragment.setArguments(args);
				registrationSurveyFragment.show(getSupportFragmentManager(), "questions");
			}
		}

	}

	@Override
	public void onFinishRegistrationSurvey(CreateParticipantSurvey survey) {

		new SaveRegistrationSurveyTask().execute(survey);
		survey.setCompleted();

	}

	private class SaveRegistrationSurveyTask extends AsyncTask<CreateParticipantSurvey, Integer, Boolean> {

		private ProgressDialog progressDialog;

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			progressDialog = ProgressDialog.show(StartActivity.this, "", getString(R.string.saving_data), true, false);
		}

		@Override
		protected Boolean doInBackground(CreateParticipantSurvey... params) {

			int participantId = PreferenceManager.getDefaultSharedPreferences(StartActivity.this).getInt(
					"participantId", -1);

			boolean success = params[0].saveToXml(participantId);
			if (success) {
				Log.i(TAG, "Successfully stored registration survey data on SD card.");
			} else {
				Log.i(TAG, "Unable to store registration survey data on SD card.");
			}

			return success;
		}

		@Override
		protected void onPostExecute(Boolean success) {
			super.onPostExecute(success);

			if (success) {
				Toast.makeText(StartActivity.this, R.string.data_saved_successfully, Toast.LENGTH_SHORT).show();
			} else {
				Toast.makeText(StartActivity.this, R.string.error_saving_data, Toast.LENGTH_SHORT).show();
			}

			progressDialog.dismiss();
		}

	}

	private boolean isEverythingReady() {
		if (!Util.servicesConnected(this)) {
			return false;
		}

		if (!loginOK()) {
			Toast.makeText(this, getString(R.string.log_in_or_use_test_mode), Toast.LENGTH_SHORT).show();
			return false;
		}

		if (!Util.hasConnection(this)) {
			Toast.makeText(this, getString(R.string.no_internet_connection), Toast.LENGTH_SHORT).show();
			return false;
		}

		return true;
	}

	private boolean loginOK() {
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		boolean testMode = inTestMode();
		boolean loggedIn = prefs.getInt("participantId", -1) > 0;

		return testMode || loggedIn;
	}

}
